<?php
namespace Sfsn\Core\Util;

use Sfsn\Core\Exception\Exception;

class Filter
{

    /**
     * 输入类型
     *
     * @var array
     */
    public static $input = [
        'get' => INPUT_GET,
        'post' => INPUT_POST,
        'cookie' => INPUT_COOKIE,
        'server' => INPUT_SERVER,
        'env' => INPUT_ENV
    ];

    /**
     * Filter自带的验证类型
     *
     * @var array
     */
    public static $filters = [
        // Validate filters
        /**
         * FILTER_VALIDATE_BOOLEAN 过滤器把值作为布尔选项来验证。
         * Name: "boolean"
         * ID-number: 258
         * 可能的返回值：
         * 如果是 "1", "true", "on" 以及 "yes"，则返回 true。
         * 如果是 "0", "false", "off", "no" 以及 ""，则返回 false。
         * 否则返回 NULL。
         */
        'boolean' => FILTER_VALIDATE_BOOLEAN,
        
        /**
         * FILTER_VALIDATE_EMAIL 过滤器把值作为电子邮件地址来验证。
         * Name: "validate_email"
         * ID-number: 274
         */
        'validate_email' => FILTER_VALIDATE_EMAIL,
        
        /**
         * FILTER_VALIDATE_FLOAT 过滤器把值作为浮点数来验证。
         * Name: "float"
         * ID-number: 259
         */
        'float' => FILTER_VALIDATE_FLOAT,
        
        /**
         * FILTER_VALIDATE_INT 过滤器以整数验证值。
         * Name: "int"
         * ID-number: 257
         * 可能的选项或标志：
         * min_range - 规定最小整数值
         * max_range - 规定最大整数值
         * FILTER_FLAG_ALLOW_OCTAL - 允许八进制数值
         * FILTER_FLAG_ALLOW_HEX - 允许十六进制数值
         */
        'int' => FILTER_VALIDATE_INT,
        
        /**
         * FILTER_VALIDATE_IP 过滤器把值作为 IP 进行验证。
         * Name: "validate_ip"
         * ID-number: 275
         * 可能的标志：
         * FILTER_FLAG_IPV4 - 要求值是合法的 IPv4 IP（比如 255.255.255.255）
         * FILTER_FLAG_IPV6 - 要求值是合法的 IPv6 IP（比如 2001:0db8:85a3:08d3:1319:8a2e:0370:7334）
         * FILTER_FLAG_NO_PRIV_RANGE - 要求值是 RFC 指定的私域 IP （比如 192.168.0.1）
         * FILTER_FLAG_NO_RES_RANGE - 要求值不在保留的 IP 范围内。该标志接受 IPV4 和 IPV6 值。
         */
        'validate_ip' => FILTER_VALIDATE_IP,
        
        /**
         * FILTER_VALIDATE_MAC 验证是否为mac地址
         * Name: "validate_mac"
         * ID-number: 276
         */
        'validate_mac_address' => FILTER_VALIDATE_MAC,
        
        /**
         * FILTER_VALIDATE_REGEXP 过滤器根据兼容 Perl 的正则表达式来验证值。
         * Name: "validate_regexp"
         * ID-number: 272
         * 可能的选项：
         * regexp - 验证所依据的正则表达式
         */
        'validate_regexp' => FILTER_VALIDATE_REGEXP,
        
        /**
         * FILTER_VALIDATE_URL 过滤器把值作为 URL 进行验证。
         * Name: "validate_url"
         * ID-number: 273
         * 可能的标志：
         * FILTER_FLAG_SCHEME_REQUIRED - 要求 URL 是 RFC 兼容 URL。（比如：http://example）
         * FILTER_FLAG_HOST_REQUIRED - 要求 URL 包含主机名（http://www.example.com）
         * FILTER_FLAG_PATH_REQUIRED - 要求 URL 在主机名后存在路径（比如：eg.com/example1/）
         * FILTER_FLAG_QUERY_REQUIRED - 要求 URL 存在查询字符串（比如："eg.php?age=37"）
         */
        'validate_url' => FILTER_VALIDATE_URL,
        
        // Sanitize filters
        /**
         * FILTER_SANITIZE_EMAIL 过滤器删除字符串中所有非法的 e-mail 字符。
         * 该过滤器允许所有的字符、数字以及 $-_.+!*'{}|^~[]`#%/?@&=。
         * Name: "email"
         * ID-number: 517
         */
        'email' => FILTER_SANITIZE_EMAIL,
        
        /**
         * FILTER_SANITIZE_ENCODED 过滤器去除或 URL 编码不需要的字符。
         * 该函数与 urlencode() 函数很类似。
         * Name: "encoded"
         * ID-number: 514
         * 可能的选项或标志：
         * FILTER_FLAG_STRIP_LOW - 去除 ASCII 值在 32 以下的字符
         * FILTER_FLAG_STRIP_HIGH - 去除 ASCII 值在 32 以上的字符
         * FILTER_FLAG_ENCODE_LOW - 编码 ASCII 值在 32 以下的字符
         * FILTER_FLAG_ENCODE_HIGH - 编码 ASCII 值在 32 以上的字符
         */
        'encoded' => FILTER_SANITIZE_ENCODED,
        
        /**
         * FILTER_SANITIZE_MAGIC_QUOTES 过滤器对字符串执行 addslashes() 函数。
         * 该过滤器在预定义的字符串前面设置反斜杠。
         * 预定义字符是：
         * single quote (')
         * double quote (")
         * backslash (\)
         * NULL
         * FILTER_SANITIZE_MAGIC_QUOTES 信息：
         * Name: "number_float"
         * ID-number: 521
         */
        'magic_quotes' => FILTER_SANITIZE_MAGIC_QUOTES,
        
        /**
         * FILTER_SANITIZE_NUMBER_FLOAT 过滤器删除浮点数中所有非法的字符。
         * 该过滤器默认允许所有数字以及 +-。
         * Name: "number_float"
         * ID-number: 520
         * 可能的标志：
         * FILTER_FLAG_ALLOW_FRACTION - 允许小数分隔符 （比如 .）
         * FILTER_FLAG_ALLOW_THOUSAND - 允许千分隔符 （比如 ,）
         * FILTER_FLAG_ALLOW_SCIENTIFIC - 允许科学技术法 （比如 e 和 E）
         */
        'number_float' => FILTER_SANITIZE_NUMBER_FLOAT,
        
        /**
         * FILTER_SANITIZE_NUMBER_INT 过滤器删除数字中所有非法的字符。
         * 该过滤器允许所有数字以及 +-。
         * Name: "number_int"
         * ID-number: 519
         */
        'number_int' => FILTER_SANITIZE_NUMBER_INT,
        
        /**
         * FILTER_SANITIZE_SPECIAL_CHARS 过滤器对特殊字符进行 HTML 转义。
         * 该过滤器用于对 "<>& 以及 ASCII 值在 32 值以下的字符进行转义。
         * Name: "special_chars"
         * ID-number: 515
         * 可能的标志：
         * FILTER_FLAG_STRIP_LOW - 去除 ASCII 值在 32 以下的字符
         * FILTER_FLAG_STRIP_HIGH - 去除 ASCII 值在 32 以上的字符
         * FILTER_FLAG_ENCODE_HIGH - 编码 ASCII 值在 32 以上的字符
         */
        'special_chars' => FILTER_SANITIZE_SPECIAL_CHARS,
        
        /**
         * FILTER_SANITIZE_FULL_SPECIAL_CHARS 把一些预定义的字符转换为 HTML实体，相当于htmlspecialchars($str, ENT_QUOTES)把。
         * Name: "full_special_chars"
         * ID-number: 522
         */
        'full_special_chars' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
        
        /**
         * FILTER_SANITIZE_STRING 过滤器去除或编码不需要的字符。
         * 这个过滤器删除那些对应用程序有潜在危害的数据。它用于去除标签以及删除或编码不需要的字符。
         * Name: "string"
         * ID-number: 513
         * 可能的选项或标志：
         * FILTER_FLAG_NO_ENCODE_QUOTES - 该标志不编码引号
         * FILTER_FLAG_STRIP_LOW - 去除 ASCII 值在 32 以下的字符
         * FILTER_FLAG_STRIP_HIGH - 去除 ASCII 值在 32 以上的字符
         * FILTER_FLAG_ENCODE_LOW - 编码 ASCII 值在 32 以下的字符
         * FILTER_FLAG_ENCODE_HIGH - 编码 ASCII 值在 32 以上的字符
         * FILTER_FLAG_ENCODE_AMP - 把 & 字符编码为 &amp;
         */
        'string' => FILTER_SANITIZE_STRING,
        
        /**
         * FILTER_SANITIZE_STRIPPED 过滤器去除或编码不需要的字符。
         * 该过滤去是 FILTER_SANITIZE_STRING 过滤器的别名。
         * 这个过滤器删除那些对应用程序有潜在危害的数据。它用于去除标签或删除或编码不需要的字符。
         * Name: "string"
         * ID-number: 513
         * 可能的选项或标志：
         * FILTER_FLAG_NO_ENCODE_QUOTES - 该标志不编码引号
         * FILTER_FLAG_STRIP_LOW - 去除 ASCII 值在 32 以下的字符
         * FILTER_FLAG_STRIP_HIGH - 去除 ASCII 值在 32 以上的字符
         * FILTER_FLAG_ENCODE_LOW - 编码 ASCII 值在 32 以下的字符
         * FILTER_FLAG_ENCODE_HIGH - 编码 ASCII 值在 32 以上的字符
         * FILTER_FLAG_ENCODE_AMP - 把 & 字符编码为 &amp;
         */
        'stripped' => FILTER_SANITIZE_STRIPPED,
        
        /**
         * FILTER_SANITIZE_URL 过滤器删除字符串中所有非法的 URL 字符。
         * 该过滤器允许所有的字符、数字以及 $-_.+!*'(),{}|\\^~[]`"><#%;/?:@&=。
         * Name: "url"
         * ID-number: 518
         */
        'url' => FILTER_SANITIZE_URL,
        
        /**
         * FILTER_UNSAFE_RAW 过滤器不进行任何过滤，去除或编码特殊字符。
         * 这个过滤器删除那些对应用程序有潜在危害的数据。它用于去除标签以及删除或编码不需要的字符。
         * 如果不规定标志，则该过滤器没有任何行为。
         * Name: "unsafe_raw"
         * ID-number: 516
         */
        'unsafe_raw' => FILTER_UNSAFE_RAW,
        
        // 回调
        /**
         * FILTER_CALLBACK 过滤器使用用户自定义函数对值进行过滤。
         * 这个过滤器为我们提供了对数据过滤的完全控制。
         * 指定的函数必须存入名为 "options" 的关联数组中。
         * Name: "callback"
         * ID-number: 1024
         */
        'callback' => FILTER_CALLBACK
    ];

    /**
     * 支持过滤列表
     */
    private static function filterList()
    {
        return filter_list();
    }

    /**
     * 验证类型
     *
     * @param string $type            
     */
    public static function has($type)
    {
        $type = self::strul($type);
        $filter_list = self::filterList();
        return array_search($type, $filter_list) !== false ? true : false;
    }

    /**
     * 验证是否有数据
     * 
     * @param string $type            
     * @param string $variable            
     * @throws Exception
     * @return bool
     */
    public static function hasVar($type, $variable)
    {
        $type = self::strul($type);
        if (array_key_exists($type, self::$input)) {
            return filter_has_var($type, $variable);
        } else {
            throw new Exception($type . '数据接收方式不存在');
        }
    }

    /**
     * 变量验证
     *
     * filterVar($var,[$filter,[$options]])
     * 使用过滤器$filter过滤$var变量，如果$filter可以使用配置参数，可以第三个参数$options里面配置
     *
     * @param unknown $variable
     *            需要验证的变量
     * @param string $filter
     *            验证规则
     * @param string $options
     *            规则选项
     * @return bool
     */
    public static function filterVar($variable, $filter = null, $options = null)
    {
        $options = [
            'options' => $options
        ];
        $filter = self::$filters[self::strul($filter)];
        return filter_var($variable, $filter, $options);
    }

    /**
     * 数组验证
     *
     * filter_var_array($data,$filter)
     * $data为被过滤的变量，$filter为过滤器，两者通过key来对应，看下面这个例子你可以很容易的明白这个函数的用图
     *
     * $data = array(
     * 'product_id' => 'libgd<script>',
     * 'component' => '10',
     * 'versions' => '2.0.33',
     * 'testscalar' => array('2', '23', '10', '12'),
     * 'testarray' => '2',
     * );
     * $args = array(
     * 'product_id' => FILTER_SANITIZE_ENCODED,
     * 'component' => array('filter' => FILTER_VALIDATE_INT,
     * 'flags' => FILTER_FORCE_ARRAY,
     * 'options' => array('min_range' => 1, 'max_range' => 10)
     * ),
     * 'versions' => FILTER_SANITIZE_ENCODED,
     * 'doesnotexist' => FILTER_VALIDATE_INT,
     * 'testscalar' => array(
     * 'filter' => FILTER_VALIDATE_INT,
     * 'flags' => FILTER_REQUIRE_SCALAR,
     * ),
     * 'testarray' => array(
     * 'filter' => FILTER_VALIDATE_INT,
     * 'flags' => FILTER_FORCE_ARRAY,
     * )
     * );
     * $myinputs = filter_var_array($data, $args);
     * var_dump($myinputs);
     *
     * @param array $data
     *            需要验证的数据
     * @param array $filter
     *            验证规则
     */
    public static function filterArray($data, $filter)
    {
        return filter_var_array($data, $filter);
    }

    /**
     * 输入验证
     *
     * fileter_input(INPUT_GET,'huanggy',FILTER_VALIDATE_INT);
     * 使用过滤器$filter过滤$variable_name变量，如果$filter可以使用配置参数，可以第四个参数$options里面配置
     *
     * @param string $type            
     * @param unknown $variable_name            
     * @param string $filter            
     * @param string $options            
     * @return bool
     */
    public static function filterInput($type, $variable_name, $filter = null, $options = null)
    {
        $type = self::$input[self::strul($type)];
        $options = [
            'options' => $options
        ];
        $filter = self::$filters[self::strul($filter)];
        return filter_input($type, $variable_name, $filter, $options);
    }

    /**
     * 验证get数据
     * 
     * @param unknown $variable_name            
     * @param string $filter            
     * @param string $options            
     */
    public static function filterGet($variable_name, $filter = null, $options = null)
    {
        self::filterInput('get', $variable_name, $filter, $options);
    }

    /**
     * 验证post数据
     * 
     * @param unknown $variable_name            
     * @param string $filter            
     * @param string $options            
     */
    public static function filterPost($variable_name, $filter = null, $options = null)
    {
        self::filterInput('post', $variable_name, $filter, $options);
    }

    /**
     * 验证cookie数据
     * 
     * @param unknown $variable_name            
     * @param string $filter            
     * @param string $options            
     */
    public static function filterCookie($variable_name, $filter = null, $options = null)
    {
        self::filterInput('cookie', $variable_name, $filter, $options);
    }

    /**
     * 验证$_SERVER变量数据
     * 
     * @param unknown $variable_name            
     * @param string $filter            
     * @param string $options            
     */
    public static function filterServer($variable_name, $filter = null, $options = null)
    {
        self::filterInput('server', $variable_name, $filter, $options);
    }

    /**
     * 验证$_ENV变量数据
     * 
     * @param unknown $variable_name            
     * @param string $filter            
     * @param string $options            
     */
    public static function filterEnv($variable_name, $filter = null, $options = null)
    {
        self::filterInput('env', $variable_name, $filter, $options);
    }

    /**
     * 字符串大小写转换
     * 
     * @param unknown $variable
     *            待转换字符
     * @param string $type
     *            lower小写 upper大写 默认lower
     * @return string
     */
    private static function strul($variable, $type = 'lower')
    {
        if ($type == 'lower') {
            return strtolower($variable);
        } else {
            return strtoupper($variable);
        }
    }
}

